package Wallpaper::Client;
$VERSION = sprintf "%.3f", 1 + substr( q$Revision: 159 $, 9 ) / 1000;

use Storable qw( dclone store retrieve );

use Wallpaper::Main qw( $mux $sock $version );
use Wallpaper::Configuration qw( %datastore );
use Wallpaper::Randomizer qw( randomfile );
use Wallpaper::Tools qw( decision_tree timedata getdir shifty );

use strict;

my $general_config = Wallpaper::Configuration->general;
my $debug          = $general_config->{ 'debug' };

my %clients = ();

sub new {
    my $package       = shift;
    my $this_function = ( caller( 0 ) )[3];
    my $self = bless { mux => shift,
                       fh  => shift
                     } => $package;

    # Register the new caller object as the callback specifically for
    # this file handle.

    $self->{ mux }->set_callback_object( $self, $self->{ fh } );

    # Register this player object in the main list of players
    $clients{ $self } = $self;

    $mux->set_timeout( $self->{ fh }, 1 );

    #$mux->write( $self->{ fh } , "wallpaper version $version by cmauch READY\n");
    print "\n$self : $this_function (1) New Connection\n" if $debug;
} ## end sub new

sub clients { return values %clients; }

sub mux_input {
    my $self = shift;
    shift;
    shift;    # These two args are boring
    my $input = shift;    # Scalar reference to the input

    # Process each line in the input, leaving partial lines
    # in the input buffer
    while ( $$input =~ s/^(.*?)\n// ) {
        $self->process_command( $1 );
    }
} ## end sub mux_input

sub mux_close {
    my $self          = shift;
    my $this_function = ( caller( 0 ) )[3];

    # Client disconnected;
    # [Notify other clients or something...]
    delete $clients{ $self };
    $mux->remove( $self->{ fh } );
    print "$self : $this_function (1) Connection closed\n" if $debug;
} ## end sub mux_close

# This gets called every 5 mins, not really using it yet.
sub mux_timeout {
    my $self = shift;
    my $mux  = shift;

    $self->heartbeat;    # Heartbeat every 5 mins
    $mux->set_timeout( $self->{ fh }, 300 );
}

sub mux_eof {
    my $self          = shift;
    my $mux           = shift;
    my $fh            = shift;
    my $this_function = ( caller( 0 ) )[3];

    print "$self : $this_function (1) Client disconnected, EOF\n" if $debug;
    $mux->shutdown( $self->{ fh }, 1 );
} ## end sub mux_eof

sub process_command {
    my ( $self, $input ) = @_;
    my ( $command, $param, $param2, $param3, $param4 ) = split( ':', $input );

    my $this_function = ( caller( 0 ) )[3];
    print "$self : $this_function (1) Processing command : \"$command\"\n" if $debug;

    if ( $command =~ /^\-?\-?urxvt$/ ) {
        my $alldisplay = Wallpaper::Configuration->all_display;

        $mux->write( $self->{ fh }, "UPDATE : $param : $alldisplay->{ $param }{ 'urxvt' }{ 'dir' }/urxvt" . $param . ".xpm\nEND\n" );

    } elsif ( $command =~ /^\-?\-?version$/ ) {

        $mux->write( $self->{ fh }, "Version information:\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::Main $Wallpaper::Main::VERSION\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::Loader $Wallpaper::Loader::VERSION\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::SanityCheck $Wallpaper::SanityCheck::VERSION\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::Daemon $Wallpaper::Daemon::VERSION\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::Client $Wallpaper::Client::VERSION\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::Randomizer $Wallpaper::Randomizer::VERSION\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::Tools $Wallpaper::Tools::VERSION\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::Configuration $Wallpaper::Configuration::VERSION\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::Execute::Scaling $Wallpaper::Execute::Scaling::VERSION\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::Execute::Themer $Wallpaper::Execute::Themer::VERSION\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::Execute::Mangle $Wallpaper::Execute::Mangle::VERSION\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::Execute::Collage $Wallpaper::Execute::Collage::VERSION\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::Execute::BlurryTerminal $Wallpaper::Execute::BlurryTerminal::VERSION\n" );
        $mux->write( $self->{ fh }, "\tWallpaper::Execute::ConfigTweak $Wallpaper::Execute::ConfigTweak::VERSION\nEND\n" );

    } elsif ( $command =~ /^\-?\-?status$/ ) {
        print "$self : $this_function (1) Sending wallpaper status\n" if $debug;
        $datastore{ 'status' } = "run" if ( !$datastore{ 'status' } );

        $mux->write( $self->{ fh }, "STATUS: is $datastore{'status'}\nEND\n" );

    } elsif ( $command =~ /^\-?\-?toggle$/ ) {
        print "$self : $this_function (1) Toggle wallpaper status\n" if $debug;
        $datastore{ 'status' } = "run" if ( !$datastore{ 'status' } );

        if ( $datastore{ 'status' } eq "run" ) {
            $datastore{ 'status' } = "stop";
        } else {
            $datastore{ 'status' } = "run";
        }

        # Toggle Status in global hash
        $mux->write( $self->{ fh }, "STATUS: is now toggled to $datastore{'status'}\nEND\n" );

    } elsif ( $command =~ /^\-?\-?schemedata$/ ) {
        if ( !$param ) {
            $mux->write( $self->{ fh }, "Non Fatal Error: No display set, defaulting to 0.0\n" );
            $param = "0.0";
        }
        print "$self : $this_function (1) Sending current scriptable color scheme in script format\n" if $debug;
        $mux->write( $self->{ fh },
                     "SCHEME:"
                       . $datastore{ $param }{ 'scheme' }{ 'primary' }{ 'base' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'primary' }{ 'dark' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'primary' }{ 'pale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'primary' }{ 'lpale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'warm' }{ 'base' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'warm' }{ 'dark' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'warm' }{ 'pale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'warm' }{ 'lpale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'warmer' }{ 'base' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'warmer' }{ 'dark' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'warmer' }{ 'pale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'warmer' }{ 'lpale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'warmest' }{ 'base' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'warmest' }{ 'dark' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'warmest' }{ 'pale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'warmest' }{ 'lpale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'cool' }{ 'base' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'cool' }{ 'dark' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'cool' }{ 'pale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'cool' }{ 'lpale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'cooler' }{ 'base' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'cooler' }{ 'dark' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'cooler' }{ 'pale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'cooler' }{ 'lpale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'coolest' }{ 'base' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'coolest' }{ 'dark' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'coolest' }{ 'pale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'coolest' }{ 'lpale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp' }{ 'base' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp' }{ 'dark' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp' }{ 'pale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp' }{ 'lpale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_warm' }{ 'base' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_warm' }{ 'dark' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_warm' }{ 'pale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_warm' }{ 'lpale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_warmer' }{ 'base' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_warmer' }{ 'dark' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_warmer' }{ 'pale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_warmer' }{ 'lpale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_cool' }{ 'base' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_cool' }{ 'dark' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_cool' }{ 'pale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_cool' }{ 'lpale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_cooler' }{ 'base' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_cooler' }{ 'dark' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_cooler' }{ 'pale' } . ":"
                       . $datastore{ $param }{ 'scheme' }{ 'comp_cooler' }{ 'lpale' }
                       . "\nEND\n"
                   );

        } elsif ( $command =~ /^\-?\-?scheme$/ ) {
            if ( !$param ) {
                $mux->write( $self->{ fh }, "Non Fatal Error: No display set, defaulting to 0.0\n" );
                $param = "0.0";
            }
            print "$self : $this_function (1) Sending current scriptable color scheme in human format\n" if $debug;

            # Get Color Scheme
            $mux->write( $self->{ fh }, "Basic Colors\n" );
            $mux->write( $self->{ fh }, "\tBase      : $datastore{ $param }{ 'scheme' }{'primary'}{'base'}\n" );
            $mux->write( $self->{ fh }, "\tDark      : $datastore{ $param }{ 'scheme' }{'primary'}{'dark'}\n" );
            $mux->write( $self->{ fh }, "\tPale      : $datastore{ $param }{ 'scheme' }{'primary'}{'pale'}\n" );
            $mux->write( $self->{ fh }, "\tLess pale : $datastore{ $param }{ 'scheme' }{'primary'}{'lpale'}\n" );
            $mux->write( $self->{ fh }, "Warm Colors\n" );
            $mux->write( $self->{ fh }, "\tBase      : $datastore{ $param }{ 'scheme' }{'warm'}{'base'}\n" );
            $mux->write( $self->{ fh }, "\tDark      : $datastore{ $param }{ 'scheme' }{'warm'}{'dark'}\n" );
            $mux->write( $self->{ fh }, "\tPale      : $datastore{ $param }{ 'scheme' }{'warm'}{'pale'}\n" );
            $mux->write( $self->{ fh }, "\tLess pale : $datastore{ $param }{ 'scheme' }{'warm'}{'lpale'}\n" );
            $mux->write( $self->{ fh }, "Warmer Colors\n" );
            $mux->write( $self->{ fh }, "\tBase      : $datastore{ $param }{ 'scheme' }{'warmer'}{'base'}\n" );
            $mux->write( $self->{ fh }, "\tDark      : $datastore{ $param }{ 'scheme' }{'warmer'}{'dark'}\n" );
            $mux->write( $self->{ fh }, "\tPale      : $datastore{ $param }{ 'scheme' }{'warmer'}{'pale'}\n" );
            $mux->write( $self->{ fh }, "\tLess pale : $datastore{ $param }{ 'scheme' }{'warmer'}{'lpale'}\n" );
            $mux->write( $self->{ fh }, "Warmest Colors\n" );
            $mux->write( $self->{ fh }, "\tBase      : $datastore{ $param }{ 'scheme' }{'warmest'}{'base'}\n" );
            $mux->write( $self->{ fh }, "\tDark      : $datastore{ $param }{ 'scheme' }{'warmest'}{'dark'}\n" );
            $mux->write( $self->{ fh }, "\tPale      : $datastore{ $param }{ 'scheme' }{'warmest'}{'pale'}\n" );
            $mux->write( $self->{ fh }, "\tLess pale : $datastore{ $param }{ 'scheme' }{'warmest'}{'lpale'}\n" );
            $mux->write( $self->{ fh }, "Cool Colors\n" );
            $mux->write( $self->{ fh }, "\tBase      : $datastore{ $param }{ 'scheme' }{'cool'}{'base'}\n" );
            $mux->write( $self->{ fh }, "\tDark      : $datastore{ $param }{ 'scheme' }{'cool'}{'dark'}\n" );
            $mux->write( $self->{ fh }, "\tPale      : $datastore{ $param }{ 'scheme' }{'cool'}{'pale'}\n" );
            $mux->write( $self->{ fh }, "\tLess pale : $datastore{ $param }{ 'scheme' }{'cool'}{'lpale'}\n" );
            $mux->write( $self->{ fh }, "Cooler Colors\n" );
            $mux->write( $self->{ fh }, "\tBase      : $datastore{ $param }{ 'scheme' }{'cooler'}{'base'}\n" );
            $mux->write( $self->{ fh }, "\tDark      : $datastore{ $param }{ 'scheme' }{'cooler'}{'dark'}\n" );
            $mux->write( $self->{ fh }, "\tPale      : $datastore{ $param }{ 'scheme' }{'cooler'}{'pale'}\n" );
            $mux->write( $self->{ fh }, "\tLess pale : $datastore{ $param }{ 'scheme' }{'cooler'}{'lpale'}\n" );
            $mux->write( $self->{ fh }, "Coolest Colors\n" );
            $mux->write( $self->{ fh }, "\tBase      : $datastore{ $param }{ 'scheme' }{'coolest'}{'base'}\n" );
            $mux->write( $self->{ fh }, "\tDark      : $datastore{ $param }{ 'scheme' }{'coolest'}{'dark'}\n" );
            $mux->write( $self->{ fh }, "\tPale      : $datastore{ $param }{ 'scheme' }{'coolest'}{'pale'}\n" );
            $mux->write( $self->{ fh }, "\tLess pale : $datastore{ $param }{ 'scheme' }{'coolest'}{'lpale'}\n" );
            $mux->write( $self->{ fh }, "Complementary Basic Colors\n" );
            $mux->write( $self->{ fh }, "\tBase      : $datastore{ $param }{ 'scheme' }{'comp'}{'base'}\n" );
            $mux->write( $self->{ fh }, "\tDark      : $datastore{ $param }{ 'scheme' }{'comp'}{'dark'}\n" );
            $mux->write( $self->{ fh }, "\tPale      : $datastore{ $param }{ 'scheme' }{'comp'}{'pale'}\n" );
            $mux->write( $self->{ fh }, "\tLess pale : $datastore{ $param }{ 'scheme' }{'comp'}{'lpale'}\n" );
            $mux->write( $self->{ fh }, "Complementary Warm Colors\n" );
            $mux->write( $self->{ fh }, "\tBase      : $datastore{ $param }{ 'scheme' }{'comp_warm'}{'base'}\n" );
            $mux->write( $self->{ fh }, "\tDark      : $datastore{ $param }{ 'scheme' }{'comp_warm'}{'dark'}\n" );
            $mux->write( $self->{ fh }, "\tPale      : $datastore{ $param }{ 'scheme' }{'comp_warm'}{'pale'}\n" );
            $mux->write( $self->{ fh }, "\tLess pale : $datastore{ $param }{ 'scheme' }{'comp_warm'}{'lpale'}\n" );
            $mux->write( $self->{ fh }, "Complementary Warmer Colors\n" );
            $mux->write( $self->{ fh }, "\tBase      : $datastore{ $param }{ 'scheme' }{'comp_warmer'}{'base'}\n" );
            $mux->write( $self->{ fh }, "\tDark      : $datastore{ $param }{ 'scheme' }{'comp_warmer'}{'dark'}\n" );
            $mux->write( $self->{ fh }, "\tPale      : $datastore{ $param }{ 'scheme' }{'comp_warmer'}{'pale'}\n" );
            $mux->write( $self->{ fh }, "\tLess pale : $datastore{ $param }{ 'scheme' }{'comp_warmer'}{'lpale'}\n" );
            $mux->write( $self->{ fh }, "Complementary Cool Colors\n" );
            $mux->write( $self->{ fh }, "\tBase      : $datastore{ $param }{ 'scheme' }{'comp_cool'}{'base'}\n" );
            $mux->write( $self->{ fh }, "\tDark      : $datastore{ $param }{ 'scheme' }{'comp_cool'}{'dark'}\n" );
            $mux->write( $self->{ fh }, "\tPale      : $datastore{ $param }{ 'scheme' }{'comp_cool'}{'pale'}\n" );
            $mux->write( $self->{ fh }, "\tLess pale : $datastore{ $param }{ 'scheme' }{'comp_cool'}{'lpale'}\n" );
            $mux->write( $self->{ fh }, "Complementary Cooler Colors\n" );
            $mux->write( $self->{ fh }, "\tBase      : $datastore{ $param }{ 'scheme' }{'comp_cooler'}{'base'}\n" );
            $mux->write( $self->{ fh }, "\tDark      : $datastore{ $param }{ 'scheme' }{'comp_cooler'}{'dark'}\n" );
            $mux->write( $self->{ fh }, "\tPale      : $datastore{ $param }{ 'scheme' }{'comp_cooler'}{'pale'}\n" );
            $mux->write( $self->{ fh }, "\tLess pale : $datastore{ $param }{ 'scheme' }{'comp_cooler'}{'lpale'}\nEND\n" );

        } elsif ( $command =~ /^\-?\-?next$/ ) {

            # Start next scheduled photo
            if ( $datastore{ 'status' } eq "stop" ) {
                $mux->write( $self->{ fh }, "Wallpaper run process is stopped.\nEND\n" );
              last;
            }

            my $time       = Wallpaper::Tools->timedata( localtime );
            my $photodir   = $general_config->{ 'bgdir' } . "/" . $general_config->{ 'photoset' }[ $time->{ 'hour' } ];
            my @items      = qw( 0 1 2 3 4 );
            my $alldisplay = Wallpaper::Configuration->all_display;

            for my $primary ( @items ) {
                for my $secondary ( @items ) {
                    my $display = $primary . "." . $secondary;
                    if ( $alldisplay->{ $display }{ 'enable' } ) {

                        my @toselect = getdir( $photodir );
                        my $selected = randomfile( \@toselect, $display, $photodir );    # Pick a new image for upcoming photo
                        my $next     = $photodir . "/" . $selected;           # Full path to new image
                        shifty( $next, $display );
                    }
                    if ( $alldisplay->{ $display }{ 'gtk' }{ 'enable' } ) {
                        if ( $datastore{ $display }{ 'gtk' } eq "Wallie1" ) {
                            $datastore{ $display }{ 'gtk' } = "Wallie2";
                        } elsif ( $datastore{ $display }{ 'gtk' } eq "Wallie2" ) {
                            $datastore{ $display }{ 'gtk' } = "Wallie1";
                        } else {
                            $datastore{ $display }{ 'gtk' } = "Wallie1";
                        }
                    } ## end if ( $alldisplay->{ $display...

                } ## end for my $secondary ( @items)
            } ## end for my $primary ( @items)

            $mux->write( $self->{ fh }, "OK: Looks good, forking render process ...\nEND\n" );    # Disconnect client too

            $SIG{ CHLD } = \&REAPER;                                                              # reap ALL children

            if ( !defined( my $kidpid = fork() ) ) {

                # fork returned undef, so failed
                die "Cannot fork: $!";
            } elsif ( $kidpid == 0 ) {

                # fork returned 0, so this branch is child
                &decision_tree( $self );
                exit;
            }

        } elsif ( $command =~ /^\-?\-?current$/ ) {
            if ( $param ) {
                $mux->write( $self->{ fh }, "$datastore{ $param }{ 'current' }\nEND\n" );
            } else {
                $mux->write( $self->{ fh }, "Non Fatal Error: No display set, defaulting to 0.0\n" );
                $mux->write( $self->{ fh }, "$datastore{ '0.0' }{ 'current' }\nEND\n" );
            }

        } elsif ( $command =~ /^\-?\-?photo$/ ) {

            if ( ! $param ) {
                $mux->write( $self->{ fh }, "ERROR: A complete pathname to a photo is required.  Try --help:photo\nEND\n" );
                last;
            }

            if ( ( $param ) and ( ! $param2 )) {
                $param2 = $param;
                $param  = "0.0";
                $mux->write( $self->{ fh }, "Non Fatal Error: No display set, defaulting to $param\n" );
            }

            print "$self : $this_function (1) Queing photo build of $param2 on $param\n" if $debug;

            if ( -e $param2 ) {
                $mux->write( $self->{ fh }, "OK: Wallpaper exists ... queued.\nEND\n" );
                shifty( $param2, $param );
            } else {
                $mux->write( $self->{ fh }, "ERROR: Wallpaper file does not exit, screw you....\nEND\n" );
            }

        } elsif ( $command =~ /^\-?\-?photodir$/ ) {
            
            if ( ! $param ) {
                $mux->write( $self->{ fh }, "ERROR: A complete pathname to a directory is required.  Try --help:photodir\nEND\n" );
                last;
            }

            if ( ( $param ) and ( ! $param2 )) {
                $mux->write( $self->{ fh }, "Non Fatal Error: No display set, defaulting to 0.0\n" );
                $param2 = $param;
                $param  = "0.0";
            }
            if ( -d $param2 ) {

                print "$self : $this_function (1) Queing photo build of a photo $param2 on $param\n" if $debug;
                my @toselect = getdir( $param2 );
                my $selected = randomfile( \@toselect, $param, $param2 );    # Pick a new image for upcoming photo
                my $next     = $param2 . "/" . $selected;                    # Full path to new image
                shifty( $next, $param );

                $mux->write( $self->{ fh }, "OK: Looks good, and I chose $next for the next image to display on $param ...\nEND\n" );    # Disconnect client too

            } else {
                $mux->write( $self->{ fh }, "ERROR: Wallpaper directory does not exist ...\nEND\n" );    # Disconnect client too
            }
        
        } elsif ( $command =~ /^\-?\-?restart$/ ) {
            print "$self : $this_function (1) Reloading configuration data\n" if $debug;
            $mux->write( $self->{ fh }, "OK: Reloading configuration data\nEND\n" );
            &Wallpaper::Tools::phoenix;

        } elsif ( $command =~ /^\-?\-?die$/ ) {
            print "$self : $this_function (1) Sending die response\n" if $debug;
            $mux->write( $self->{ fh }, "OK: Killing daemon\nEND\n" );

            store \%datastore, '/tmp/wallpaper.dump'; # Store current datastore info to disk before killing ourself.

            print "Cleaning up PID ... Byebye!\n";
            unlink( "$ENV{'HOME'}/.wall.pid" );
            exit( 0 );

        } elsif ( $command =~ /\?|\-?\-?help|\-h/ ) {
            print "$self : $this_function (1) Sending help file\n" if $debug;

            if ( !$param ) {
                $mux->write(
                    $self->{ fh }, "Possible options are:
  status, toggle, schemedata, scheme, next, photo, photodir, current, 
  version, restart, or die

  for detailed help, try adding the parameter to the help command, eg:
  wallpaper --help:photo displays detailed info on using the photo option.
  
  PLEASE NOTE THAT ALL PARAMETERS ARE SEPERATED BY A \":\" and not a space!\nEND\n"
                           );

            } elsif ( $param =~ /^version$/ ) {
                $mux->write(
                    $self->{ fh }, "Version help:
  Version shows the current versions of all the pieces which when put together
  form this program.  The version number is basicly a simple conversion between the
  subversion revision number, and a CPAN style version.  So a subversion revision
  of 350 would result in a module version of 1.350.  When the subversion revision
  hits 1000, the CPAN version will roll over to 2.0.  This should NOT indicate that
  any major functionality has been added.

  In general, the version information is useful for debugging purposes only.\nEND\n"
                           );

            } elsif ( $param =~ /^status$/ ) {
                $mux->write(
                    $self->{ fh }, "Status help:

  Involking status prints the current \"run\" status of the stript.  The run status
  can be toggled with the --toggle command.\nEND\n"
                           );
            } elsif ( $param =~ /^toggle$/ ) {
                $mux->write(
                    $self->{ fh }, "Toggle help:

  Involking toggle changes the current \"run\" status of the script.  This allows
  you to quickly disable the use of the --next command which is typically called
  by a cron job.  Executing --toggle again disabled the lock on the run status.

  You can retrieve the current status with the --status parameter.\nEND\n"
                           );

            } elsif ( $param =~ /^schemedata$/ ) {
                $mux->write(
                    $self->{ fh }, "Scheme Data help:
  This returns the color scheme, each value delimited by a \":\".  A parameter of
  the display to return a scheme for is optional.  The default is to return the
  color scheme for the 0.0 display.
  
  For a description of each color, try --scheme\nEND\n"
                           );
            } elsif ( $param =~ /^scheme$/ ) {
                $mux->write(
                    $self->{ fh }, "Scheme help:
  This returns the current color scheme in human readable format.  A parameter of
  the display to return a scheme for is optional.  The default is to return the
  color scheme for the 0.0 display.\nEND\n"
                           );
            } elsif ( $param =~ /^next$/ ) {
                $mux->write(
                    $self->{ fh }, "Next help:
  Next commands the daemon to process the next photos in the queue right away.

  This should normally be called by cron, but you might consider binding this
  command to a hotkey for quick background generation\nEND\n"
                           );

            } elsif ( $param =~ /^photo$/ ) {
                $mux->write(
                    $self->{ fh }, "Photo help:
  Photo preselects the next image to render, and adds it to the queue.  It
  requires one parameter, the photo to enqueue.  Prefix the complete pathname to
  the wallpaper with an optional displayname to pick a monitor to queue for.  EG:

  wallie --photo:/path/to/wallpaper.png
  wallie --photo:0.1:/path/to/wallpaper.png

  This command does NOT start a render process.  use --next to begin that
  process.  If you wish to select a wallpaper and render it right away, try:

  wallie --photo:/path/to/wallpaper.png ; wallie --next\nEND\n"
                           );

            } elsif ( $param =~ /^photodir$/ ) {
                $mux->write(
                    $self->{ fh }, "Photodir help:
  Photodir preselects a random image from a user defined directory.  This allows a
  onetime over-ride of the time-based directory based randomization selection found
  in the configuration file.  It requries one parameter, the complete pathname to
  a directory to pick a random file for.  Prefix the filename with an optional
  displayname no pick a monitor to queue for.  EG:

  wallie --photodir:/path/to/wallpapers
  wallie --photodir:0.1:/path/to/wallpapers\nEND\n"
                           );

            } elsif ( $param =~ /^current/ ) {
                $mux->write(
                    $self->{ fh }, "Current help:
  Current displays the photograph (with path) that the currently displayed photo was
  originally rendered from.  This is useful for outside scripts usually.
  
  A parameter of the display to return a photo for is required, else the scheme for 
  0.0 will be returned.\nEND\n"
                           );
            } elsif ( $param =~ /^restart/ ) {
                $mux->write(
                    $self->{ fh }, "Restart help:
  Restart kills the current running daemon, and immediately starts a new process.
  This is useful when you have updated your configuration, and want too see the
  changes go into effect.  EG:

  wallie --restart\nEND\n"
                           );
            } elsif ( $param =~ /^die/ ) {
                $mux->write(
                    $self->{ fh }, "Die help:
  This kills the daemon process.  You should use this command to terminate the
  daemon rather than just killing it off.

  IF YOU KILL THE DAEMON MANUALLY, you will lose your current configuration scheme,
  and current/next photo information!  Always use DIE!

  If you wish to restart the daemon because (for example, your configuration has
  changed), try: wallie --restart. \nEND\n"
                           );
            } ## end elsif ( $param =~ /^die/ )

        } else {
            print "$self : $this_function (1) Unknown command, sending response\n" if $debug;
            $mux->write( $self->{ fh }, "ERROR: Dunno what you mean\nEND\n" );
        }
    } ## end elsif ( $command =~ /^\-?\-?schemedata$/)

    sub heartbeat {
        my $self          = shift;
        my $this_function = ( caller( 0 ) )[3];

        $clients{ $self }{ 'timer' }++;
        print "$self : $this_function (1) heartbeat $clients{$self}{'timer'} OK\n" if $debug;
    }

    # Clean up forked render children

    sub REAPER {
        my $waitedpid     = wait;                                    # reap the child
        my @items         = qw( 0 1 2 3 4 );
        my $alldisplay    = Wallpaper::Configuration->all_display;
        my $this_function = ( caller( 0 ) )[3];

        for my $primary ( @items ) {
            for my $secondary ( @items ) {
                my $display = $primary . "." . $secondary;
                if ( $alldisplay->{ $display }{ 'urxvt' }{ 'enable' } ) {
                    for my $list ( keys %clients ) {
                        print "$clients{ $list } : $this_function (1) Sending urxvt update for display $display\n" if $debug;
                        $mux->write( $clients{ $list }->{ fh },
                                     "UPDATE : $display : $alldisplay->{ $display }{ 'urxvt' }{ 'dir' }/urxvt" . $display . ".xpm\n" );
                    }
                }
            } ## end for my $secondary ( @items)
        } ## end for my $primary ( @items)
        for my $primary ( @items ) {
            for my $secondary ( @items ) {
                my $display = $primary . "." . $secondary;
                if ( $alldisplay->{ $display }{ 'urxvt' }{ 'enable' } ) {
                    for my $list ( keys %clients ) {
                        $mux->write( $clients{ $list }->{ fh }, "\nEND\n" );
                    }
                }
            } ## end for my $secondary ( @items)
        } ## end for my $primary ( @items)

        # Read in scheme data stored externally
        for my $primary ( @items ) {
            for my $secondary ( @items ) {
                my $display = $primary . "." . $secondary;
                if ( -e "/tmp/wallpaper-scheme-$display.dump" ) {
                    print "                                  : $this_function (1) Collecting Scheme data for display $display\n" if $debug;
                    my $storable = retrieve("/tmp/wallpaper-scheme-$display.dump");
                    # IF nothing in storable?
                    $datastore{ $display }{ 'scheme' } = dclone( $storable );
                }
            } ## end for my $secondary ( @items)
        } ## end for my $primary ( @items)

        $SIG{ CHLD } = \&Wallpaper::Client::REAPER;
    } ## end sub REAPER

    1;
